import util from './util';

const defaults = {
  url: '',
  name: '', // 上传文件的name，默认会获取input上面的name值
  data: {}, // 同时需上传的数据，目前只接受 序列化Array和Object
  auto: true, // 是否自动上传
  element: null, // input
  dragDrop: null, // 拖拽区域
  maxSize: '2M',
  accept: '', // 限制文件类型，默认获取element上的accept
  multiple: false, // 是否支持一次上传多个，可以写数字，2 => 代表最多上传2个
  partSize: 0, // 分片上传时每片的大小，默认是0，不分片上传
  partIndex: 0, // 分片上传时，开始上传的序号默认是第一片
  resend: false, // 断网造成的上传失败，是否在联网后自动上传

  init: $.noop, // 初始化完成后执行
  select: $.noop, // 选择文件后执行，监听change
  warning: $.noop, // 选择文件后，出现错误触发回调
  progress: $.noop, // 进度条回调 ,参数：(loaded,total)
  complete: $.noop, // 上传完成后回调
  success: $.noop, // 上传成功后回调 对于分片上传需要一个promise
  error: $.noop, // 上传失败后回调
};

class Up {
  constructor(options) {
    this.options = $.extend(defaults, options);
    this.element = this.options.element;
    this.vaildRule = {};
    this.files = [];
    this.uploading = false; // 是否正在上传
    this.inputName = this.element.attr('name') || this.options.name; // input的name
    this.build();
    this.options.init.call(this);
  }
  build() {
    const { options } = this;

    const maxSize = util.getByte(options.maxSize) || Infinity;
    let maxCount = 1; // 不接受0，写0代表的是 Infinity
    if (this.element.attr('multiple') !== undefined) {
      maxCount = Number(this.element.attr('data-max-count')) || Infinity;
    } else if (typeof options.multiple === 'number') {
      maxCount = options.multiple || Infinity;
    } else if (options.multiple === true) {
      maxCount = Infinity;
    }

    if (maxCount > 1) {
      this.multiple = true;
      this.element.attr('multiple', 'multiple');
    }
    let accept = this.element.attr('accept');
    if (!accept && options.accept) {
      this.element.attr('accept', options.accept);
      accept = options.accept;
    }
    const fileType = accept || new RegExp(`(${accept.replace(/\s*,\s*/g, '|')})$`, 'i');

    this.vaildRule = { maxSize, maxCount, fileType, leftCount: maxCount };

    this.errorMsg = {
      required: '请选择文件！',
      max_size: `文件大小不能超过${options.maxSize}！`,
      file_type: '请上传正确格式的文件！',
      uploading: '文件正在上传，请勿重复操作！',
      max_count: `最多只能选择${maxCount}个！`,
      left_count: '已超过剩余上传的个数！',
      repeat: '不能选择相同文件！',
    };

    this.bindEvent();
  }
  bindEvent() {
    this.element.on('change', e => {
      const files = Array.from(e.target.files);
      const $dom = $(e.target);
      const reselect = $dom.data('reselect'); // 重新选择 的src
      reselect && $dom.data('reselect', ''); // 清除存储
      files && this.selectFun(files, reselect);
    });
    // 拖拽
    if (this.options.dragDrop) {
      this.bindDragDrop();
    }
  }
  bindDragDrop() { // 绑定拖拽事件
    const preventAction = e => {
      e.stopPropagation();
      e.preventDefault();
    };
    this.options.dragDrop.on('dragenter dragover dragleave', e => preventAction(e)).on('drop', e => {
      preventAction(e);
      // 如果是jq对象需要从originalEvent获取event
      const dom = e.dataTransfer || e.originalEvent.dataTransfer;
      const files = Array.from(dom.files);
      files && this.selectFun(files);
    });
  }
  selectFun(files, reselect) {
    this.clear();
    reselect && URL.revokeObjectURL(reselect); // 清理url
    const oldFiles = this.files || [];
    if (!this.valid(oldFiles, files, reselect)) { return; }
    const arr = files.map(file => ({
      src: URL.createObjectURL(file),
      name: file.name,
      file,
    }));
    if (oldFiles.length && this.multiple) {
      if (reselect) {
        this.files = oldFiles.map(v => (v.src === reselect ? arr[0] : v)); // 这里只能替换一个
      } else {
        this.files = oldFiles.concat(arr);
      }
    } else { // 第一次上传
      this.files = arr;
    }
    this.options.select(arr, this.files);
    if (this.options.auto) { // 是否自动上传
      this.upload();
    }
  }
  valid(oldFiles, files, reselect) {
    const { maxSize, maxCount, fileType } = this.vaildRule;
    let leftCount = maxCount - oldFiles.length;
    leftCount = leftCount >= 0 ? leftCount : 0;
    if (reselect || !this.multiple) { // 重新上传只能传一个, 非多文件上传时也是一个
      leftCount = 1;
    }
    if (!files.length) {
      this.warningHandling('required');
      return false;
    } else if (files.length > leftCount) {
      this.warningHandling('left_count');
      return false;
    }
    const repeatFile = files.find(v => oldFiles.findIndex(val => val.file.name === v.name && val.file.size === v.size && val.file.lastModified === v.lastModified) > -1);
    if (repeatFile) {
      this.warningHandling('repeat', repeatFile.file);
      return false;
    }
    if (files.length > maxCount) {
      this.warningHandling('max_count');
      return false;
    }
    for (let i = 0; i < files.length; i++) {
      let file = files[i];
      if (maxSize && file.size > maxSize) {
        this.warningHandling('max_size', file);
        return false;
      }
      if (typeof fileType instanceof RegExp && !fileType.test(file.name)) {
        this.warningHandling('fileType', file);
        return false;
      }
    }
    return true;
  }
  warningHandling(key, file) {
    this.options.warning.call(this, {
      type: key,
      msg: this.errorMsg[key],
      file,
    });
  }
  remove(src, callback) { // 删除单个file
    if (this.files instanceof Array) {
      this.files = this.files.filter(v => v.src !== src);
      URL.revokeObjectURL(src); // 清理
    }
    callback && callback.call(this, this.files);
    return this;
  }
  removeAll() { // 删除所有的file
    if (this.files instanceof Array && this.files.length) {
      this.files.forEach(v => URL.revokeObjectURL(v.src));
    }
    this.files = [];
    return this;
  }
  reselect(src) {
    this.element.data('reselect', src).trigger('click');
    return this;
  }
  upload(opt) {
    if (this.uploading) {
      this.warningHandling('uploading');
      return;
    }
    this.uploading = true;

    if (opt) { // 外部调用，可以传参
      this.options = { ...this.options, ...opt };
    }
    if (this.options.partSize > 0) { // 分片上传
      this.ajaxPartFile().then(() => {
        this.uploading = false;
      }).catch(() => {
        this.uploading = false;
      });
    } else {
      this.ajaxFile().then(() => {
        this.uploading = false;
        // this.removeAll();
      }).catch(() => {
        this.uploading = false;
        // this.removeAll();
      });
    }
    return this;
  }
  ajaxFile() {
    return new Promise((resolve, reject) => {
      const { options } = this;
      let resData = options.data;
      if (util.isObject(resData)) {
        resData = Object.keys(resData).map(v => ({ name: v, value: resData[v] }));
      }
      this.files.forEach(v => {
        resData.push({ name: this.inputName, value: v.file });
      });
      util.request({
        url: options.url,
        data: resData,
        resend: options.resend,
        progress: options.progress,
        complete: options.complete(),
        success: (data) => {
          options.success(data);
          resolve(data);
        },
        error(xhr, status) {
          options.error(xhr, status);
          reject(xhr, status);
        },
      });
    });
  }
  ajaxPartFile() { // 分片上传
    return new Promise((resolve, reject) => {
      const { options } = this;
      const { file } = this.files[0];
      const resData = {
        partSize: options.partSize,
        partIndex: options.partIndex,
        partTotalNum: 1,
        ...options.data,
      };

      function ajaxPart() {
        const { partSize, partIndex } = resData;
        const start = partIndex * partSize;
        const end = start + partSize;
        resData[this.inputName] = file.slice(start, end);
        resData.key = `${resData.key}_${partIndex}`;
        resData.partTotalNum = Math.ceil(file.size / partSize);

        util.ajax({
          url: options.url,
          data: resData,
          resend: options.resend,
          complete: options.complete,
          progress(num) {
            let per = ((num / 100) + partIndex + 1) * (100 / resData.partTotalNum);
            per = per.toFixed(2);
            options.progress(per > 99 ? 99 : per);
          },
          success: (result) => {
            options.success(result, { ...resData, defaultKey: options.data.key });
            if (partIndex + 1 < resData.partTotalNum) {
              resData.partIndex = partIndex + 1;
              ajaxPart();
            } else {
              resolve(result);
            }
          },
          error: (xhr, status) => {
            options.error(xhr, status);
            reject(xhr, status);
          },
        });
      }
      ajaxPart();
    });
  }
  clear() { // 清空file的value
    const file = this.element;
    if (util.versionIE() <= 10) {
      const newFile = file.clone(true).val('');
      file.remove();
      this.element = newFile.insertAfter(this.element);
    } else {
      this.element.val('');
    }
    return this;
  }
  destroy() {
    this.element.off();
  }
}

export default Up;
